/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */
/*
 * Created on Jun 13, 2004
 *
 */
package edu.mit.csail.relo.jdt.parts;

import java.util.List;
import java.util.Map;

import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.PredicateUtils;
import org.apache.log4j.Logger;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FlowLayout;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.ToolbarLayout;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Color;
import org.openrdf.model.Resource;
import org.openrdf.sesame.sail.StatementIterator;

import edu.mit.csail.relo.eclipse.gef.IncrementalGraphicalEditPart;
import edu.mit.csail.relo.figures.CodeUnitFigure;
import edu.mit.csail.relo.figures.ToolbarCompartmentFigure;
import edu.mit.csail.relo.jdt.CodeUnit;
import edu.mit.csail.relo.jdt.RJCore;
import edu.mit.csail.relo.jdt.ReloJDTPlugin;
import edu.mit.csail.relo.modelBridge.Artifact;
import edu.mit.csail.relo.modelBridge.DirectedRel;
import edu.mit.csail.relo.parts.ArtifactEditPart;
import edu.mit.csail.relo.parts.ReloController;
import edu.mit.csail.relo.store.ReloRdfRepository;
import edu.mit.csail.relo.store.StoreUtil;

/**
 * @author vineet
 *  
 */
public class ClassEditPart extends CompartmentedCodeUnitEditPart {
	static final Logger logger = ReloJDTPlugin.getLogger(ClassEditPart.class);

	public static Color classColor = new Color(null, 255, 255, 206);

    {
        compartmentTypes.add(RJCore.fieldType);
        compartmentTypes.add(RJCore.methodType);
        compartmentTypes.add(RJCore.interfaceType);

        compartmentGenerator.put(RJCore.fieldType, FieldCompartmentCodeUnit.class);
        compartmentGenerator.put(RJCore.methodType, MethodCompartmentCodeUnit.class);
        compartmentGenerator.put(RJCore.interfaceType, ClassCompartmentCodeUnit.class);
    }


    // detail level
	final int labelWithPublicChildren = labelWithCompartments + 1;
	final int labelWithProtectedChildren = labelWithCompartments + 2;
	final int labelWithAllChildren = labelWithCompartments + 3;
	
	/* (non-Javadoc)
	 * @see edu.mit.csail.relo.parts.ArtifactEditPart#getDLStr(int)
	 */
	@Override
    public String getDLStr(int dl) {
	    if (dl == labelWithPublicChildren)
	        return "labelWithPublicChildren";
	    else if (dl == labelWithProtectedChildren)
	        return "labelWithProtectedChildren";
	    else if (dl == labelWithAllChildren)
	        return "labelWithAllChildren";
	    return super.getDLStr(dl);
	}

	public @Override int getDefaultDL() {
		//return labelWithPublicChildren;
		return labelWithCompartments;
	}

	public @Override int getMaximumDL() {
		return labelWithAllChildren;
	}

	
	@Override
    protected IFigure createFigure(IFigure curFig, int newDL) {
		if (curFig == null) {
			curFig = new CodeUnitFigure(getLabel(), getCU().getIcon(getRepo()), classColor, true);

            // right align more button
            IFigure moreFig = new Figure();
            FlowLayout layout = new FlowLayout(/*isHorizontal*/true);
            layout.setMajorAlignment(ToolbarLayout.ALIGN_BOTTOMRIGHT);
            moreFig.setLayoutManager(layout);
            moreFig.add(getMoreBtn());
            
            curFig.add(moreFig);
		}

		return curFig;
	}
    @Override
    protected String getChildrenLabel() {
        return "Members";
    }
    
    
	@Override
    protected void updateMembers(int newDL) {
        //logger.debug("updateMembers: " + getDLStr(newDL));
        super.updateMembers(newDL);
	    CodeUnit cu = getCU();
	    ReloRdfRepository repo = getRepo();

	    if (newDL >= currDL) {
		    if (newDL >= labelWithPublicChildren)
		        realizeChildrenArtifacts(cu.getChildrenArtifacts(repo, CodeUnit.getAccessFilter(repo, RJCore.publicAccess)));
	        if (newDL >= labelWithProtectedChildren)
		        realizeChildrenArtifacts(cu.getChildrenArtifacts(repo, CodeUnit.getAccessFilter(repo, RJCore.protectedAccess)));
	        if (newDL >= labelWithAllChildren)
		        realizeChildrenArtifacts(cu.getChildrenArtifacts(repo, CodeUnit.getAccessFilter(repo, RJCore.privateAccess)));
	    }
	    if (newDL < currDL) {
	        if (newDL < labelWithAllChildren)
	            removeChildrenArtifacts(cu.getChildrenArtifacts(repo, CodeUnit.getAccessFilter(repo, RJCore.privateAccess)));
	        if (newDL < labelWithProtectedChildren)
	            removeChildrenArtifacts(cu.getChildrenArtifacts(repo, CodeUnit.getAccessFilter(repo, RJCore.protectedAccess)));
	        if (newDL < labelWithPublicChildren)
	            removeChildrenArtifacts(cu.getChildrenArtifacts(repo, CodeUnit.getAccessFilter(repo, RJCore.publicAccess)));
	    }
	}

    
    
    
    // layout support for nested classes
    /*
    @Override
    public void contributeNodesToGraph(Graph graph, Subgraph sg, Map<AbstractGraphicalEditPart,Object> partsToNodesMap) {
        CompartmentEditPart nestedClassesEP = compartmentMap.get(RJCore.classType);
        if (nestedClassesEP == null || nestedClassesEP.getChildren().size() == 0) {
            contributeNodeToGraph(graph, sg, partsToNodesMap);
            return;
        }
        //contributeNodeToGraph(graph, sg, partsToNodesMap);

        try {
        // nested children: we have a subgraph embedded
        Subgraph mySG = nestedClassesEP.contributeSubgraphToGraph(graph, sg, partsToNodesMap);
        //contributeChildrenToGraph(graph, mySG, partsToNodesMap);
        for (int i = 0; i < nestedClassesEP.getChildren().size(); i++) {
            AbstractReloEditPart cuep = (AbstractReloEditPart) nestedClassesEP.getChildren().get(i);
            cuep.contributeNodesToGraph(graph, mySG, partsToNodesMap);
        }
        } catch (Throwable t) {
            logger.info("Unexepected", t);
        }
    }

    @Override
    protected void applyOwnResults(Graph graph, Map partsToCellMap) {
        CompartmentEditPart nestedClassesEP = compartmentMap.get(RJCore.classType);
        if (nestedClassesEP == null || nestedClassesEP.getChildren().size() == 0) {
            super.applyOwnResults(graph, partsToCellMap);
            return;
        }

        Cell n = (Cell) partsToCellMap.get(this);
        int extraTop = getNonNestedHeight();
        Rectangle nesedClassBounds = n.getBounds();
        nesedClassBounds.y += extraTop;
        nesedClassBounds.height -= extraTop;
        nestedClassesEP.getFigure().setBounds(n.getBounds());
        
        for (int i = 0; i < getSourceConnections().size(); i++) {
            AbstractReloRelationPart rel = (AbstractReloRelationPart) getSourceConnections().get(i);
            rel.applyGraphResults(graph, partsToCellMap);
        }
    }
    @Override
    protected void applyChildrenResults(Graph graph, Map partsToCellMap) {
        CompartmentEditPart nestedClassesEP = compartmentMap.get(RJCore.classType);
        if (nestedClassesEP == null || nestedClassesEP.getChildren().size() == 0) {
            return;
        }
        for (int i = 0; i < nestedClassesEP.getChildren().size(); i++) {
            AbstractReloEditPart part = (AbstractReloEditPart)nestedClassesEP.getChildren().get(i);
            part.applyGraphResults(graph, partsToCellMap);
        }
    }


    @Override
    protected Insets contributeSubgraghInsets() {
        // only used when we have nested children
        Insets insets = super.contributeSubgraghInsets();
        insets.top += getNonNestedHeight();
        //return insets;
        return insets
                .getAdded(GraphLayoutManager.PADDING)
                .getAdded(GraphLayoutManager.PADDING)
                .getAdded(GraphLayoutManager.PADDING);

    }
    protected int getNonNestedHeight() {
        int extraTop = 0;
        CompartmentEditPart nestedClassesEP = compartmentMap.get(RJCore.classType);
        for (CompartmentEditPart cep : compartmentMap.values()) {
            if (cep == nestedClassesEP) continue;
            extraTop += cep.getFigure().getBounds().height;
        }
        return extraTop;
    }
    */

    
    
    
    
    @Override
    public void buildContextMenu(IMenuManager menu) {
        super.buildContextMenu(menu);
        
	    final CodeUnitEditPart cuep = this;
        final ClassEditPart classCUEP = this;
        final CodeUnit cu = cuep.getCU();
		IAction action;
		
		action = new Action("Show Supertype Hierarchy") {
		    @Override
            public void run() {
		        try {
		    		ReloController vc = (ReloController) cuep.getRoot().getContents();
		    		StatementIterator supTypesIt = cu.getSupertypesHierarchyCU(getRepo()).getStatements(null, null, null);
		    		ReloRdfRepository storeModel = getRepo();
		    		Resource res;
		    		while (supTypesIt.hasNext()) {
		    		    org.openrdf.model.Statement stmt = supTypesIt.next();

		    		    res = StoreUtil.getResource(storeModel, stmt.getSubject()); 
		    		    ArtifactEditPart baseTypeAEP = vc.createOrFindQueriedArtifactEditPart(res);

        	            res = StoreUtil.getResource(storeModel, (Resource) stmt.getObject()); 
        	            ArtifactEditPart supTypeAEP = vc.createOrFindQueriedArtifactEditPart(res);
                        
                        if (baseTypeAEP != null && supTypeAEP != null)
                            vc.addRel(baseTypeAEP, RJCore.inherits, supTypeAEP);
		    		}
                } catch (Exception e) {
                    logger.error("Unexpected exception", e);
                }
		    }};
		menu.appendToGroup("main", action);
	
		action = new Action("Show Classes being Extended") {
		    @Override
            public void run() {
		        try {
		    		ReloController vc = (ReloController) cuep.getRoot().getContents();
		    		CodeUnit supTypeCU = cu.getExtendedTypeCU(cuep.getRepo());
		    		ArtifactEditPart supTypeBaseAEP = cuep;
		    		while (supTypeCU != null) {
		    			ArtifactEditPart supTypeAEP = vc.createOrFindQueriedArtifactEditPart(supTypeCU);
        	            if (supTypeAEP == null) break;
                        vc.addRel(supTypeBaseAEP, RJCore.inherits, supTypeAEP);

                        supTypeCU = supTypeCU.getExtendedTypeCU(cuep.getRepo());
                        supTypeBaseAEP = supTypeAEP;
		    		}
                } catch (Exception e) {
                    logger.error("Unexpected exception", e);
                }
		    }};
		menu.appendToGroup("main", action);
	
        menu.appendToGroup("main", cuep.getRelAction("Show Extending Classes", DirectedRel.getRev(RJCore.inherits)));
        menu.appendToGroup("main", cuep.getRelAction(
                "Show Interfaces being Implemented", 
                DirectedRel.getFwd(RJCore.inherits), 
                CodeUnit.getTypeFilter(cuep.getRepo(), RJCore.interfaceType)));
        menu.appendToGroup("main", cuep.getRelAction("Show Referencing Types+Methods", DirectedRel.getRev(RJCore.refType)));

		action = new Action("Show Containing Package") {
		    @Override
            public void run() {
                CompoundCommand actionCmd = new CompoundCommand();
                cuep.realizeParent(actionCmd, /*inferring*/ false);
                if (actionCmd.size() > 0) ClassEditPart.this.execute(actionCmd);
		}};
		menu.appendToGroup("main", action);
        
        action = new Action("Hide Class") {
            @Override
            public void run() {
                CompoundCommand actionCmd = new CompoundCommand();
                actionCmd.add(new Command("hide class") {
                    ReloController rc = null;
                    IncrementalGraphicalEditPart parentIGEP = null;
                    @Override
                    public void execute() { 
                        if (rc == null) rc = getRootController();
                        parentIGEP = classCUEP.hideAEP();
                    }
                    @Override
                    public void undo() {
                        rc.addNode(classCUEP, parentIGEP);
                        classCUEP.assertParenthood();
                    }
                });
                //cuep.realizeParent(actionCmd, /*inferring*/ false);
                ClassEditPart.this.execute(actionCmd);
            }
        };
        menu.appendToGroup("main", action);
	}
    

    private static abstract class ArtifactCompartmentEditPart extends CompartmentEditPart {
        @Override
        public String getLabel(Artifact art, Artifact contextArt) {
            return CodeUnit.getLabel(getRepo(), art, contextArt);
        }
        @Override
        protected ImageDescriptor getIconDescriptor(Artifact art, Resource resType) {
            return CodeUnit.getIconDescriptor(getRepo(), art, resType);
        }
    }
    private static abstract class ToolbarCompartmentEditPart extends ArtifactCompartmentEditPart {
		@Override
        protected IFigure createFigure(IFigure curFig, int newDL) {
			if (curFig == null) {
				curFig = new ToolbarCompartmentFigure();
			}
			return curFig;
		}
		@Override
        public void suggestDetailLevelIncrease() {
			if (currDL == getMinimalDL()) {
				realizeChildrenArtifacts(getArtifact().getNonDerivedBaseArtifact().getChildrenArtifacts(getRepo(), CodeUnit.getAccessFilter(getRepo(), RJCore.publicAccess)));
				currDL++;
			}
		}
	}

	private final static class MethodCompartmentCodeUnit extends CompartmentCodeUnit {
        public MethodCompartmentCodeUnit(CodeUnit cu) {
            super(cu);
        }

        @Override
        public ArtifactEditPart createController() {
            ArtifactEditPart methodCompartment = new ToolbarCompartmentEditPart() {
                @Override
                protected String getChildrenLabel() {
                    return "Methods";
                }
        		@Override
                public String toString() {
        			return "methodCompartment" + tag.getTrailer();
        		}
                @Override
                public void registerTypes(Map<Resource, CompartmentEditPart> compartmentMap) {
                    compartmentMap.put(RJCore.methodType, this);
                }
            };
            return methodCompartment;
        }

        @Override
        public List<Artifact> getChildrenArtifacts(ReloRdfRepository repo, Predicate filterPred) {
            if (filterPred == null) filterPred = PredicateUtils.truePredicate();
        	return super.getChildrenArtifacts(repo, PredicateUtils.andPredicate(
        	        filterPred,
        	        CodeUnit.getTypeFilter(repo, RJCore.methodType)
        	        ));
        }
    }

    private static final class FieldCompartmentCodeUnit extends CompartmentCodeUnit {
        public FieldCompartmentCodeUnit(CodeUnit cu) {
            super(cu);
        }

        @Override
        public ArtifactEditPart createController() {
            ArtifactEditPart attributeCompartment = new ToolbarCompartmentEditPart() {
                @Override
                protected String getChildrenLabel() {
                    return "Fields";
                }
        		@Override
                public String toString() {
        			return "filedsCompartment" + tag.getTrailer();
        		}
                @Override
                public void registerTypes(Map<Resource, CompartmentEditPart> compartmentMap) {
                    compartmentMap.put(RJCore.fieldType, this);
                }
            };
            return attributeCompartment;
        }

        @Override
        public List<Artifact> getChildrenArtifacts(ReloRdfRepository repo, Predicate filterPred) {
            if (filterPred == null) filterPred = PredicateUtils.truePredicate();
        	return super.getChildrenArtifacts(repo, PredicateUtils.andPredicate(
        	        filterPred,
        	        CodeUnit.getTypeFilter(repo, RJCore.fieldType)
        	        ));
        }
    }


    private static final class ClassCompartmentCodeUnit extends CompartmentCodeUnit {
        public ClassCompartmentCodeUnit(CodeUnit cu) {
            super(cu);
        }

        @Override
        public ArtifactEditPart createController() {
            ArtifactEditPart attributeCompartment = new ArtifactCompartmentEditPart() {
                @Override
                protected String getChildrenLabel() {
                    return "Class";
                }
                @Override
                public String toString() {
                    return "classCompartment" + tag.getTrailer();
                }
                @Override
                public void registerTypes(Map<Resource, CompartmentEditPart> compartmentMap) {
                    compartmentMap.put(RJCore.classType, this);
                    compartmentMap.put(RJCore.interfaceType, this);
                }
                /*
                @Override
                protected IFigure createFigure(IFigure curFig, int newDL) {
                    if (curFig == null) {
                        curFig = new Figure();
                        curFig.setLayoutManager(new GraphLayoutManager.SubgraphLayout());
                    }
                    return curFig;
                }
                */
                @Override
                protected IFigure createFigure(IFigure curFig, int newDL) {
                    if (curFig == null) {
                        curFig = new ToolbarCompartmentFigure();
                    }
                    return curFig;
                }
            };
            return attributeCompartment;
        }

        @Override
        public List<Artifact> getChildrenArtifacts(ReloRdfRepository repo, Predicate filterPred) {
            if (filterPred == null) filterPred = PredicateUtils.truePredicate();
            return super.getChildrenArtifacts(repo, PredicateUtils.andPredicate(
                    filterPred,
                    PredicateUtils.orPredicate(
                            CodeUnit.getTypeFilter(repo, RJCore.classType),
                            CodeUnit.getTypeFilter(repo, RJCore.interfaceType)
                            )
                    ));
        }
    }

}